
// Copyright (C) 2011 Luca Piccioni
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;

namespace Derm.Render
{
	/// <summary>
	/// A group of shader objects, programs and includes.
	/// </summary>
	[XmlType("ShaderModule")]
	public class ShaderLibraryModule
	{
		/// <summary>
		/// Path of the generated C# module (relative to configuration path).
		/// </summary>
		[XmlAttribute("Path")]
		public string Path;

		/// <summary>
		/// CodeNamespace used for declaring classes in this shader library module.
		/// </summary>
		[XmlAttribute("CodeNamespace")]
		public string CodeNamespace;

		/// <summary>
		/// CodeNamespace used for declaring classes in this shader library module.
		/// </summary>
		[XmlAttribute("LibNamespace")]
		public string LibNamespace;

		/// <summary>
		/// Shader objects defined by the generated C# module.
		/// </summary>
		[XmlElement("ShaderObject")]
		public ShaderLibraryObject[] ShaderObjects;

		/// <summary>
		/// Shader programs defined by the generated C# module.
		/// </summary>
		[XmlElement("ShaderProgram")]
		public ShaderLibraryProgram[] ShaderPrograms;

		/// <summary>
		/// Shader includes defined by the generated C# module.
		/// </summary>
		[XmlElement("ShaderInclude")]
		public ShaderLibraryInclude[] ShaderIncludes;

		/// <summary>
		/// 
		/// </summary>
		/// <param name="libraryConfiguration"></param>
		/// <param name="libraries"></param>
		/// <param name="overwrite"></param>
		public void Generate(ShaderLibraryConfiguration libraryConfiguration, IEnumerable<ShaderLibraryConfiguration> libraries, bool overwrite)
		{
			// Check whether the module has to be updated
			if ((File.Exists(Path) == true) && (overwrite == false)) {
				FileInfo info = new FileInfo(Path);

				if (info.LastWriteTime >= libraryConfiguration.ModificationDate) {
					bool dirty = false;

					// Module was generated after the last modification of the shader configuration, but
					// some shader object source could be modified since last update

					foreach (ShaderLibraryObject objectClass in ShaderObjects) {
						FileInfo objectSourceFileInfo = new FileInfo(objectClass.SourcePath);

						if (objectSourceFileInfo.LastWriteTime >= info.LastWriteTime) {
							dirty = true;
							break;
						}
					}

					// Do not update module (make-like behavior)
					if (dirty == false) {
						Console.WriteLine("Module '{0}' not updated because more recent than source.", Path);
						return;
					}
				}
			}

			// Check empty module
			if (((ShaderPrograms == null) || (ShaderPrograms.Length == 0)) && ((ShaderObjects == null) || (ShaderObjects.Length == 0))) {
				Console.WriteLine("Module '{0}' not generated because it is empty.", Path);
				if (File.Exists(Path) == true)
					File.Delete(Path);
				return;
			}

			// Update module
			Console.WriteLine("Updating module '{0}':", Path);

			using (FileStream fs = new FileStream(Path, FileMode.Create, FileAccess.Write)) {
				using (StreamWriter sw = new StreamWriter(fs)) {
					// First character: line feed
					sw.WriteLine();
#if false
					// Source licence
					if (SourceLicence != ModuleLicences.None) {
						try {
							using (StreamReader sr = new StreamReader(System.IO.Path.Combine(Directory.GetCurrentDirectory(), "Licences/Preamble" + SourceLicence.ToString() + ".txt"))) {
								string line;

								while ((line = sr.ReadLine()) != null)
									sw.WriteLine(line);
							}
							sw.WriteLine();
						} catch {
							Console.WriteLine("  Warning: unable to find licence preamble {0}. Continuing in module generation.", SourceLicence.ToString());
						}
					}
#endif
					// Default namespace
					if (CodeNamespace == null)
						CodeNamespace = "Derm.Render.Shaders";

					// Using declarations
					if (CodeNamespace.StartsWith("Derm.Render.Shaders") == false) {
						sw.WriteLine("using Derm.Render.Shaders;");		// Required for compiling
						sw.WriteLine();
					}

					// CodeNamespace (open)
					sw.WriteLine("namespace {0} {1}", CodeNamespace, "{");
					sw.WriteLine();

					// Shader programs
					if (ShaderPrograms != null) {
						foreach (ShaderLibraryProgram programClass in ShaderPrograms) {
							Console.WriteLine("	Defining shader program '{0}'", programClass.ClassName);
							programClass.Generate(sw, libraries, this);
						}
					}

					// Shader objects
					if (ShaderObjects != null) {
						foreach (ShaderLibraryObject objectClass in ShaderObjects) {
							Console.WriteLine("	Defining shader object '{0}'", objectClass.ClassName);
							objectClass.Generate(sw, LibNamespace);
						}
					}

					// Shader includes
					if (ShaderIncludes != null) {
						foreach (ShaderLibraryInclude includeClass in ShaderIncludes) {
							Console.WriteLine("	Defining shader include '{0}'", includeClass.ClassName);
							includeClass.Generate(sw);
						}
					}

					// CodeNamespace (close)
					sw.WriteLine("}");
					sw.WriteLine();
				}
			}
		}
	}
}
